#include <iostream>
#include <string>
#include <cstring>
#include <functional>
#include "log.hpp"
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unordered_map>
using namespace std;

// using func_t = std::function<std::string(const std::string&)>;
typedef function<string(const string &)> func_t;
Log lg;

enum
{
    SOCKET_ERR = 1,
    BIND_ERR,

};

string DefaultIp = "0.0.0.0";
uint16_t DefaultPort = 8080;
const int SIZE_ = 1024;
class Udpserver
{
public:
    Udpserver(uint16_t port = DefaultPort, string ip = DefaultIp) : ip_(ip), port_(port)
    {
    }
    void Init()
    {
        // 创建upd socket(套接字)
        sockfd_ = socket(AF_INET, SOCK_DGRAM, 0); // UDP,打开了网卡文件
        if (sockfd_ < 0)
        {
            lg(Fatal, "socket create error,socket:%d", sockfd_);
            exit(SOCKET_ERR);
        }
        lg(Info, "socket create success,socket:%d,error:%s", sockfd_, strerror(errno));

        // bind socket
        struct sockaddr_in local;
        bzero(&local, sizeof(local));       // 相当于memset
        local.sin_family = AF_INET;         // 地址族
        local.sin_port = htons(port_);      // 端口号
        local.sin_addr.s_addr = INADDR_ANY; // IP,表示绑定所有网卡，INADDR_ANY=0.0.0.0
        if (bind(sockfd_, (const struct sockaddr *)&local, sizeof(local)) < 0)
        {
            lg(Fatal, "bind socket error,socket:%d,error: %s", sockfd_, strerror(errno));
            exit(BIND_ERR);
        }
        lg(Info, "bind socket success,socket:%d", sockfd_);
    }

    // 检查用户是否在用户表中，如果没在，添加到用户表中
    void CheckUser(const struct sockaddr_in &cliaddr, const string &clientip, uint16_t clientport)
    {
        auto iter = online_users_.find(clientip);
        if (iter == online_users_.end())
        {
            online_users_.insert({clientip, cliaddr});
            cout << "[" << clientip << ":" << clientport << "]" << "add to online users" << endl;
        }
    }

    void Broadcast(char *buffer_in, const string &clientip, uint16_t clientport)
    {
        string echo_message = "[";
        echo_message += clientip;
        echo_message += ":";
        echo_message += to_string(clientport);
        echo_message += "]";
        echo_message += "send to a message:";
        echo_message += buffer_in;
        for (auto user : online_users_)
        {
            sendto(sockfd_, echo_message.c_str(), echo_message.size(), 0, (struct sockaddr *)&user.second, sizeof(user.second));
        }
    }

    void Run()
    {
        isrunning_ = true;
        char buffer_in[SIZE_];
        cout << "Running begin!" << endl;
        while (isrunning_)
        {
            struct sockaddr_in cliaddr;
            // 接收信息
            unsigned int len = sizeof(cliaddr);
            int n = recvfrom(sockfd_, buffer_in, sizeof(buffer_in) - 1, 0, (struct sockaddr *)&cliaddr, &len);
            if (n < 0)
            {
                lg(Warning, "recvfrom error, errno: %d, err string: %s", errno, strerror(errno));
                continue;
            }
            buffer_in[n] = 0;

            // 整理发消息的用户信息
            string clientip = inet_ntoa((struct in_addr)cliaddr.sin_addr); // inet_ntoa:将网络地址从二进制格式（struct in_addr）转换为点分十进制字符串格式的函数
            uint16_t clientport = ntohs(cliaddr.sin_port);
            CheckUser(cliaddr, clientip, clientport);

            // 将信息发给每个用户
            Broadcast(buffer_in,clientip,clientport);
        }
    }
    ~Udpserver()
    {
        if (sockfd_ >= 0)
            close(sockfd_);
    }

   
private:
    int sockfd_; // 网络文件描述符
    string ip_;
    uint16_t port_; // 表明服务端的端口号
    bool isrunning_;
    unordered_map<string, struct sockaddr_in> online_users_;
};